require 'open-uri'

module ESI

  class Tag
    attr_reader :buffer_callback
    attr_reader :tag_callback
    attr_reader :parent

    def initialize( tag_str, parent = nil )
      @parent = parent
      @attrs = {}
      tag_str.scan(/([\w-]+)\s*=\s*['"](.+?)['"]/) do|k,v|
        @attrs[k] = v
      end
      @buffer_callback = nil
      @tag_callback = nil
    end

    def has_attr?(key)
      @attrs.has_key?(key)
    end

    def [](key)
      @attrs[key]
    end

    def self.create( tag_str, parent = nil )
      case tag_str
      when /esi:try/
        t = TryTag.new( tag_str, parent )
      when /esi:include/
        t = IncludeTag.new( tag_str, parent )
      when /esi:attempt/
        t = AttemptTag.new( tag_str, parent )
      when /esi:except/
        t = ExceptTag.new( tag_str, parent )
      end
      t
    end
 
    def invoke( parser )
      @buffer_callback = parser.parser.buffer_ready
      @tag_callback = parser.parser.tag_ready
    end

    def end_tag( parser )
      parser.parser.buffer_ready = @buffer_callback
      parser.parser.tag_ready = @tag_callback
    end

  end

  class TryTag < Tag

    def invoke( parser )
      super
      @parser = parser

      # stop streaming content within a try tag to conform to the spec
      parser.parser.buffer_ready = lambda {|buffer| 
        puts "not streaming..."
      }

      # monitor new tags 
      parser.parser.tag_ready = lambda {|tag,start| tag_handler( tag, start ) }
    end

    def tag_handler( tag_str, start )
      puts tag_str.inspect
      if start
        tag = Tag.create(tag_str,self)
        tag.invoke( @parser )
      else
        @tag.end_tag( @parser )
      end
    end

  end

  class AttemptTag < Tag
    attr_accessor :includes

    def invoke( parser )
      super
      parser.parser.buffer_ready = @parent.buffer_callback
      puts "Invoking AttemptTag"
    end
 
  end

  class ExceptTag < Tag
    attr_accessor :includes

    def invoke( parser )
      super
      parser.parser.buffer_ready = @parent.buffer_ready
    end
 
  end

  class IncludeTag < Tag
    attr_reader :body
    attr_reader :status

    def invoke( parser )
      url = parser.url_for( self["src"] )
      failover = false

      begin
        if parser.cached?( url )
          fragment_response = parser.cache_get( url )
        else
          fragment_response = open( url, parser.fragment_params ).read
          parser.cache_store(url, self["max-age"], fragment_response)
        end
      rescue => e
        puts e.message
      rescue Exception, Timeout::Error, OpenURI::HTTPError => e
        retry_url = self["alt"]
        unless failover or retry_url.blank?
          retry_url = parser.url_for( alt )
          unless retry_url.blank? or retry_url == url
            failover = true
            url = retry_url
            retry
          end
        end
      end

      parser.parser.buffer_ready.call fragment_response
    end

  end

end
